﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.Security.Principal;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using System.Xml.Serialization;

namespace Cofe.Core.Security
{

    public sealed class GenericPrincipalPolicy : IPrincipalPolicy, IXmlSerializable
    {
        #region Constructor

        private void init(string[] grantRole, string authenticateType, string namePattern, bool isPattern)
        {
            var regex = new Regex("[\\w]*");
            foreach (var role in grantRole)
                if (!(Regex.IsMatch(role, "^[\\w]*$")))
                    throw new ArgumentException(String.Format("Role {0} is not valid", role));

            AuthenticateType = authenticateType;
            NamePattern = namePattern;
            GrantRole = grantRole;
            IsPattern = isPattern;
        }

        /// <summary>
        /// Required IsAuthenticated, also match the name pattern.
        /// </summary>
        /// <param name="authenticateType"></param>
        /// <param name="namePattern"></param>
        public GenericPrincipalPolicy(string[] grantRole, string authenticateType, string namePattern, bool isPattern = false)
        {
            init(grantRole, authenticateType, namePattern, isPattern);
        }


        /// <summary>
        /// Match everyone who authenticate by the specified method.
        /// </summary>
        public GenericPrincipalPolicy(string[] grantRole, string authenticateType)
            : this(grantRole, authenticateType, null, true)
        {

        }

        /// <summary>
        /// Match everyone.
        /// </summary>
        public GenericPrincipalPolicy(string[] grantRole)
            : this(grantRole, null)
        {

        }

        /// <summary>
        /// For Serializer only.
        /// </summary>
        public GenericPrincipalPolicy()
        {
        }

        #endregion

        #region Methods

        public override bool Equals(object obj)
        {
            if (obj is GenericPrincipalPolicy)
            {
                var objPolicy = obj as GenericPrincipalPolicy;

                foreach (var role in GrantRole)
                    if (!(objPolicy.GrantRole.Contains(role)))
                        return false;

                return GrantRole.Length == objPolicy.GrantRole.Length &&
                    AuthenticateType == objPolicy.AuthenticateType &&
                    NamePattern == objPolicy.NamePattern;
            }
            return false;
        }

        public override string ToString()
        {
            return String.Format("Role:{0} Type:{1} Name:{2}", String.Join(",", GrantRole), AuthenticateType, NamePattern);
        }

        public override int GetHashCode()
        {
            return GrantRole.GetHashCode() + (AuthenticateType + NamePattern).GetHashCode();
        }

        public bool Match(IIdentity identity)
        {
            if (AuthenticateType == null || AuthenticateType == identity.AuthenticationType)
            {
                if (!IsPattern)
                    return identity.Name == NamePattern;
                else
                    if (NamePattern == null || (identity.Name != null && Regex.IsMatch(identity.Name, NamePattern,
#if NETFX_CORE                    
                    RegexOptions.Singleline
#else
 RegexOptions.Singleline | RegexOptions.Compiled
#endif
)))
                        return true;
            }
            return false;
        }

        public System.Xml.Schema.XmlSchema GetSchema()
        {
            throw new NotImplementedException();
        }

        public void ReadXml(System.Xml.XmlReader reader)
        {
            reader.MoveToContent();
            GrantRole = reader.GetAttribute("GrantRole").Split(',');
            AuthenticateType = reader.GetAttribute("AuthenticateType");
            NamePattern = reader.GetAttribute("NamePattern");
            IsPattern = Boolean.Parse(reader.GetAttribute("IsPattern"));
        }

        public void WriteXml(System.Xml.XmlWriter writer)
        {
            writer.WriteAttributeString("GrantRole", String.Join(",", GrantRole));
            writer.WriteAttributeString("AuthenticateType", AuthenticateType);
            writer.WriteAttributeString("NamePattern", NamePattern);
            writer.WriteAttributeString("IsPattern", IsPattern.ToString());
        }


        #endregion

        #region Data

        private string _namePattern = "";

        #endregion

        #region Public Properties

        public string[] GrantRole { get; set; }

        public string AuthenticateType { get; set; }

        public bool IsPattern { get; set; }

        public string NamePattern { get { return _namePattern; } set { if (value == "") value = null; _namePattern = value; } }


        #endregion






    }
}
